{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quanlse Scheduler\n",
    "\n",
    "*Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outline\n",
    "This tutorial introduces how to use Quanlse's Scheduler to generate pulse sequences for quantum circuits. The outline of this tutorial is as follows:\n",
    "- Introduction\n",
    "- Example: generate a pulse sequence to implement a $W$-state using Quanlse‘s Scheduler\n",
    "- Summary\n",
    "\n",
    "## Introduction\n",
    "\n",
    "**Quanlse's Scheduler** is a module that allows for automatic generation of fidelity-optimized and scheduled pulse sequence for a given quantum circuit set to perform a certain quantum computing task.\n",
    "\n",
    "**Quanlse's Scheduler** has the following benefits:\n",
    "\n",
    "- Highly automatic: it generates high-fidelity pulses automatically and simultaneously minimizes the overall gate operation time.\n",
    "- Practical: it considers several limitations of the superconducting quantum system, including leakage errors, decoherence, etc.\n",
    "- Flexible: it gives users the freedom to customize qubits and circuit parameters. This can also be easily extended to other quantum computing platforms.\n",
    "\n",
    "**Quanlse's Scheduler** achieves the following goals:\n",
    "\n",
    "- It generates parameters and AWG input signal arrays for fidelity-optimized pulses when leakage into the state $|2\\rangle$ is taken into account.\n",
    "- It is also capable of scheduling pulses to minimize idle time and therefore reduce decoherence losses.\n",
    "- At the same time, it allows for the visualization of pulse sequences for the users to check the results.\n",
    "\n",
    "CNOT gate is rarely directly implemented on superconducting quantum chips. Instead, it is often constructed by piecing together single-qubit gates and other two-qubit gates like CR gate or ISWAP gate that can be easily implemented on a superconducting chip (often called native gates). The two-qubit gates that are available in the transmon-like superconducting qubit architecture can be divided into two categories:\n",
    "\n",
    "**Flux-controlled**\n",
    "\n",
    "This class of gates offers the advantage of short gate time to minimize decoherence error. However, tuning the qubit frequency can introduce flux noises and lead to the problem of frequency crowding.\n",
    "\n",
    "**All-microwave control**\n",
    "\n",
    "CR gate allows for an all-microwave control, which alleviates the problem of flux noise. However, the much longer time-scale limits the gate fidelity (because of the decoherence effects of qubits).\n",
    "\n",
    "![cnot](figures/cnot-gate.png)\n",
    "\n",
    "Since CZ gates can be used to construct a CNOT gate easily by using only two other single-qubit gates, Quanlse's Scheduler offers this way to construct a CNOT gate in a quantum circuit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Generate a pulse sequence to implement a $W$-state using Quanlse's Scheduler\n",
    "\n",
    "In 2000, Dür et al. found that a tripartite system can be entangled in a different way from GHZ (Greenberger-Horne-Zeilinger) state, which they called the $W$-state \\[4\\]:\n",
    "\n",
    "$$\n",
    "|\\Phi_{\\rm GHZ}\\rangle = \\frac{1}{\\sqrt{2}} (|000\\rangle + |111\\rangle),\n",
    "$$\n",
    "\n",
    "$$\n",
    "|\\Phi_{\\rm W}\\rangle = \\frac{1}{\\sqrt{3}} (|001\\rangle + |010\\rangle + |100\\rangle).\n",
    "$$\n",
    "\n",
    "Unlike the GHZ state, the remaining $(N-1)$-qubit state is still entangled if one of the qubit is traced out for a prepared $N$-qubit $W$-state. A three-partite $W$-state is a superposition state of three eigenstates, and only one of the particles is excited in each eigenstate. Due to its high robustness against loss, $W$-state received considerable attention in the field of quantum communication.\n",
    "\n",
    "In this example, we will demonstrate how to generate a $W$-state with Quanlse's Scheduler. Before starting the main steps, we import the numpy dependency:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy import array"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we import the dependent packages from Quanlse:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from Quanlse.QOperation.FixedGate import X, H, CNOT\n",
    "from Quanlse.QOperation.RotationGate import RY\n",
    "from Quanlse.Utils.Functions import basis\n",
    "from Quanlse.Superconduct.Simulator.PulseSim3Q import pulseSim3Q\n",
    "from Quanlse.Superconduct.SchedulerSupport.PipelineCenterAligned import centerAligned"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we go on and instantiate a `simulator` object, we usually need to first define the system configurations. For this example, we can use the built-in Quanlse three-qubit simulator `pulseSim3Q`. For more details, please refer to [multi-qubit noisy simulator](https://quanlse.baidu.com/#/doc/tutorial-multi-qubit-noisy-simulator). The system configuration(i.e. qubit frequency, coupling strength, etc) are fixed in this simulator, but the users are able to create a customed simulator object if intended to. We will use a system of the following qubits topology (each with an anharmonicity of $0.33*(2*\\pi)\\ \\textrm{GHz}$).\n",
    "\n",
    "![cnot](figures/qubitMap.png)\n",
    "\n",
    "To begin, we need to define the sampling time for the AWG. Then, we can instantiate a `pulseSim3Q` object by passing in the sampling time `dt` we had just defined."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create pulseSim3Q object\n",
    "dt = 0.01  # AWG sampling time\n",
    "model = pulseSim3Q(frameMode='lab', dt=dt)\n",
    "model.savePulse = False\n",
    "model.pipeline.addPipelineJob(centerAligned)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the `pulseSim3Q` object defined, we can proceed to constructing the quantum logic circuit. We use the following circuit to generate a $W$-state:\n",
    "\n",
    "![w](figures/w-circuit.png) \n",
    "\n",
    "where,\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "R_1 &= \\frac{1}{\\sqrt{3}} \\begin{bmatrix} \\sqrt{2} & -1 \\\\ 1 & \\sqrt{2} \\end{bmatrix}, \\\\\n",
    "R_2 &= \\begin{bmatrix} \\cos(\\pi/8) & -\\sin(\\pi/8) \\\\ \\sin(\\pi/8) & \\cos(\\pi/8) \\end{bmatrix}, \\\\\n",
    "R_3 &= \\begin{bmatrix} \\cos(\\pi/8) & \\sin(\\pi/8) \\\\ -\\sin(\\pi/8) & \\cos(\\pi/8) \\end{bmatrix}.\n",
    "\\end{align*}\n",
    "$$\n",
    "\n",
    "As we mentioned before, in superconducting quantum computing, controlled-Z gate is a native gate which can be used to implement a CNOT gate with 2 additional Hadamard gates on the target qubit.\n",
    "\n",
    "![VQE](figures/cnot-gate-hadamard.png) \n",
    "\n",
    "In this example, we use this decomposition method for the CNOT gates. In **Quanlse**, the logic quantum circuit can be defined using the following codes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define circuit\n",
    "\n",
    "# R1\n",
    "RY(-1.231)(model.Q[0])\n",
    "\n",
    "# X gate\n",
    "X(model.Q[0])\n",
    "\n",
    "# CNOT: 0 -> 1\n",
    "CNOT(model.Q[0], model.Q[1])\n",
    "\n",
    "# X gate\n",
    "X(model.Q[0])\n",
    "\n",
    "# R2\n",
    "RY(-0.785)(model.Q[2])\n",
    "\n",
    "# CNOT: 1 -> 2\n",
    "CNOT(model.Q[1], model.Q[2])\n",
    "\n",
    "# R3\n",
    "RY(0.785)(model.Q[2])\n",
    "\n",
    "# CNOT: 2 -> 1\n",
    "CNOT(model.Q[2], model.Q[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that we decompose the rotation gates $R_1, R_2$ and $R_3$ using `U(theta, phi, lambda)`, where\n",
    "$$\n",
    "U(\\theta, \\phi, \\lambda) = e^{i(\\phi/2+\\lambda/2)} R_z(\\phi) R_y(\\theta) R_z(\\lambda) =\n",
    "\\begin{bmatrix} \n",
    "    \\cos(\\theta/2) & - e^{i\\lambda} \\sin(\\theta/2) \\\\\n",
    "    e^{i\\phi} \\sin(\\theta/2) & e^{i(\\phi + \\lambda)} \\cos(\\theta/2)\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "Then, by calling the `schedule()` method, we obtain a `QJob` object which consists all the information regarding the pulse configurations. Similar to the previous tutorials, we can call the `plot()` function to visualize the complete pulse sequence. Lastly, we can obtain the unitary evolution of the simulation by calling method `runHamiltonian()` which takes a `QHam` object and a `QJob` object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Schedule\n",
    "job = model.schedule()\n",
    "job.plot(color=['red', 'green'])\n",
    "finalState = model.simulate(job=job)[0][\"state\"].T[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above pulse sequences are produced by Quanlse. When the users run this program on their local devices, Quanlse Cloud Service will return the fidelity of each individual gate that makes up the circuit.\n",
    "\n",
    "Finally, we can retrieve the final state and visualize the state populations. Knowing that the W state takes form: $|\\Phi_{\\rm W}\\rangle = \\frac{1}{\\sqrt{3}} (|001\\rangle + |010\\rangle + |100\\rangle)$, we can see below that the pulse sequence above produces a state somewhat close to the ideal W state. The small peaks are caused by noise such as leakage or crosstalk across the qubits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Calculate final state\n",
    "popList = [abs(item ** 2) for item in finalState]\n",
    "\n",
    "# Import the operator for generating basis string list\n",
    "from Quanlse.Utils.Functions import computationalBasisList\n",
    "\n",
    "# Import the function for plot bar figures\n",
    "from Quanlse.Utils.Plot import plotBarGraph\n",
    "\n",
    "# Plot the population of computational basis\n",
    "basis = computationalBasisList(3, 3)\n",
    "plotBarGraph(basis, popList, \"Population of a W state generated by QuanlseScheduler\",\n",
    "             \"Computational Basis\", \"Population\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "After reading this tutorial on Quanlse's Scheduler, the users could follow this link [tutorial-scheduler.ipynb](https://github.com/baidu/Quanlse/blob/main/Tutorial/EN/tutorial-scheduler.ipynb) to the GitHub page of this Jupyter Notebook document and run this program for themselves. The users are encouraged to try implementing other quantum circuits using a similar procedure as this tutorial.\n",
    "\n",
    "## References\n",
    "\n",
    "\\[1\\] [Krantz, Philip, et al. \"A quantum engineer's guide to superconducting qubits.\" *Applied Physics Reviews* 6.2 (2019): 021318.](https://aip.scitation.org/doi/abs/10.1063/1.5089550)\n",
    "\n",
    "\\[2\\] https://en.wikipedia.org/wiki/Quantum_optimization_algorithms\n",
    "\n",
    "\\[3\\] https://en.wikipedia.org/wiki/Quantum_algorithm\n",
    "\n",
    "\\[4\\] [Dür, Wolfgang, Guifre Vidal, and J. Ignacio Cirac. \"Three qubits can be entangled in two inequivalent ways.\" *Physical Review A* 62.6 (2000): 062314.](https://doi.org/10.1103/PhysRevA.62.062314)\n",
    "\n",
    "\\[5\\] [Guo, Guang-Can, and Yong-Sheng Zhang. \"Scheme for preparation of the W state via cavity quantum electrodynamics.\" *Physical Review A* 65.5 (2002): 054302.](https://doi.org/10.1103/PhysRevA.65.054302)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
